package cn.gemframe.oauth2.security.filter;

import java.io.IOException;
import java.util.Collection;
import java.util.UUID;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.gemframe.config.security.GemAuthProperties;
import cn.gemframe.config.security.GemTokenProperties;
import cn.gemframe.oauth2.bean.RequestPath;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpStatus;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.filter.OncePerRequestFilter;

import cn.gemframe.constant.GemConstant;
import cn.gemframe.exception.status.GemErrorStatus;
import cn.gemframe.response.ResultData;
import cn.gemframe.utils.GemStringUtlis;
import cn.gemframe.utils.GemJsonUtils;
import lombok.Data;

/**
 * Token校验过滤器
 */
@Data
@Slf4j
public class GemAccessTokenValidateFilter extends OncePerRequestFilter {

	//token存储器
	private TokenStore tokenStore;
	private RedisTemplate<String, Object> redisTemplate;
	private ValueOperations<String, Object> valueOperations;

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		//统一设置response
		response.setContentType(GemConstant.MediaType.JSON_UTF_8);

		String noRelease = UUID.randomUUID().toString();

		String requestURI = request.getRequestURI();
		log.error("TokenValidateFilter-requestURI:============={}",requestURI);
        if(requestURI == null){
            log.error("==============非法请求==============");
            return;
        }
		//token验证放行以下请求
		if (requestURI.contains(RequestPath.Access.SWAGGER)
				|| requestURI.equals(RequestPath.Access.APIDOCS)
				|| requestURI.contains(noRelease)
				|| noRelease.contains(requestURI)
		){
            filterChain.doFilter(request, response);
		} else {
			//拦截header中包含Authorization的请求
			String header = request.getHeader(GemConstant.Auth.HEADER_AUTHOR);
			//token缺失
			if (GemStringUtlis.isBlank(header) || header.equalsIgnoreCase(GemConstant.Common.NULL)) {
				response.setStatus(HttpStatus.UNAUTHORIZED.value());
				response.getWriter().write(GemJsonUtils.objectToJson
						(ResultData.getResultWithCode(GemErrorStatus.TOKEN_ISNULL)));
				return;
			}

			//header为null
			String[] split = header.split(" ");
			if (split == null || split.length < 2) {
				response.setStatus(HttpStatus.UNAUTHORIZED.value());
				response.getWriter().write(GemJsonUtils.objectToJson
						(ResultData.getResultWithCode(GemErrorStatus.TOKEN_ISNULL)));
				return;
			}

			String headerToken = split[split.length - 1];
			//比对带入token是否合法
			OAuth2AccessToken readAccessToken = tokenStore.readAccessToken(headerToken);
			if (readAccessToken == null) {
				response.setStatus(HttpStatus.UNAUTHORIZED.value());
				//redis获取
				String token = String.valueOf(valueOperations.get(headerToken));
				if (GemStringUtlis.isNotBlank(token)
						&& GemStringUtlis.isNotBlank(headerToken)
						&& headerToken.equalsIgnoreCase(token)
				) {
					redisTemplate.delete(token);
					response.getWriter().write(GemJsonUtils.objectToJson
							(ResultData.getResultWithCode(GemErrorStatus.USER_LOGINS)));
					return;
				}
				response.getWriter().write(GemJsonUtils.objectToJson
						(ResultData.getResultWithCode(GemErrorStatus.AUTHENTICATION_FAILED)));
				log.error("=============认证失败{}",1);
				return;
			}


			Collection<OAuth2AccessToken> sessionToken
					= tokenStore.findTokensByClientId(GemConstant.Auth.CLIENT_ID_NAME);
			//验证配置 认证失败
			if (sessionToken == null || sessionToken.size() == 0) {
				response.setStatus(HttpStatus.UNAUTHORIZED.value());
				response.getWriter().write(GemJsonUtils.objectToJson
						(ResultData.getResultWithCode(GemErrorStatus.AUTHENTICATION_FAILED)));
				log.error("=============认证失败{}",2);
				return;
			}

			boolean contains = sessionToken.contains(readAccessToken);
			if (!contains) {
				response.setStatus(HttpStatus.UNAUTHORIZED.value());
				response.getWriter().write(GemJsonUtils.objectToJson
						(ResultData.getResultWithCode(GemErrorStatus.AUTHENTICATION_FAILED)));
				log.error("=============认证失败{}",3);
				return;
			}
			filterChain.doFilter(request, response);
		}
	}
}
